package com.isyscore.os.metadata.service.diop;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.isyscore.device.common.util.JsonMapper;
import com.isyscore.os.core.entity.SqlQuery;
import com.isyscore.os.core.entity.SqlQueryGroup;
import com.isyscore.os.metadata.model.dto.diop.DiopResourceDto;
import com.isyscore.os.metadata.model.entity.*;
import com.isyscore.os.metadata.service.SqlQueryGroupService;
import com.isyscore.os.metadata.service.SqlQueryService;
import com.isyscore.os.metadata.service.impl.*;
import com.isyscore.os.permission.common.constants.PermissionConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import static com.isyscore.os.metadata.service.diop.DiopConstant.*;


@Service
@Slf4j
public class ExportService {

    @Autowired
    private DatamodelService datamodelService;

    @Autowired
    private ErRelationService erRelationService;

    @Autowired
    private DatamodelColumnService datamodelColumnService;

    @Autowired
    private SqlStatementService sqlStatementService;

    @Autowired
    private MetricService metricService;

    @Autowired
    private MetricGroupService metricGroupService;

    @Autowired
    private MetricDimFilterService metricDimFilterService;

    @Autowired
    private MetricMeasureService metricMeasureService;

    @Autowired
    private DataSourceServiceImpl dataSourceService;

    @Autowired
    private DIOPConfiguration diopConfiguration;

    @Autowired
    private SqlQueryService sqlQueryService;

    @Autowired
    private SqlQueryGroupService sqlQueryGroupService;

    static final String DIOP_STR="diop";

    public String doExport(List<DiopResourceDto> resources,String tenantId) {
        DateTime now = new DateTime();
        String dateStr = now.toString("yyyyMMdd");
        String timeStr = now.toString("HHmmss");
        String parentDir = this.createExportDir(dateStr, timeStr,tenantId);
        //创建自描述文件
        this.createSelfdescriptionFile(parentDir, dateStr, timeStr);
        //创建导出文件列表
        File listFile = this.createListfile(parentDir);
        List<String> exportFielPathList = Lists.newArrayList();

        Optional<DiopResourceDto> metricTopResourceOp = resources.stream().filter((resource) -> resource.getName().equalsIgnoreCase(METRIC_RESOURCE_NAME)).findFirst();
        Optional<DiopResourceDto> datamodelTopResourceOp = resources.stream().filter((resource) -> resource.getName().equalsIgnoreCase(DATAMODEL_RESOURCE_NAME)).findFirst();
        Optional<DiopResourceDto> sqlQueryResourceOp = resources.stream().filter((resource) -> resource.getName().equalsIgnoreCase(SQL_QUERY_RESOURCE_NAME)).findFirst();
        Set<Long> exportDatamodelIds = Sets.newHashSet();
        Set<Long> sqlStatementIds = Sets.newHashSet();
        if (metricTopResourceOp.isPresent()) {
            DiopResourceDto metricTopResource = metricTopResourceOp.get();
            List<DiopResourceDto> metricGroupResources = metricTopResource.getExtra();
            Set<String> groupIds = metricGroupResources.stream().map(DiopResourceDto::getName).collect(Collectors.toSet());
            //导出指标分组，不导出默认分组信息
            List<MetricGroup> metricGroups = metricGroupService.getDataByIds(groupIds).stream().filter(x->!x.getTenantId().equals(PermissionConstants.GLOBAL_BUSINESS_DATA_TENANT_ID_KEY)).peek(x->x.setTenantId(null)).collect(Collectors.toList());
            writeToFs(parentDir, METRIC_RESOURCE_NAME, METRIC_GROUP_FILE_NAME, metricGroups, exportFielPathList);
            //导出指标
            List<Metric> metrics = metricService.getMetricByGroupIds(groupIds).stream().peek(x->x.setTenantId(null)).collect(Collectors.toList());
            exportDatamodelIds.addAll(metrics.stream().map(Metric::getDatamodelRefId).collect(Collectors.toSet()));
            sqlStatementIds.addAll(metrics.stream().map(Metric::getSqlRefId).collect(Collectors.toSet()));
            writeToFs(parentDir, METRIC_RESOURCE_NAME, METRIC_FILE_NAME, metrics, exportFielPathList);
            //导出指标度量
            Set<Long> metricIds = metrics.stream().map(Metric::getId).collect(Collectors.toSet());
            List<MetricMeasure> measures = metricMeasureService.getMeasuresByMetricIds(metricIds).stream().peek(x->x.setTenantId(null)).collect(Collectors.toList());
            writeToFs(parentDir, METRIC_RESOURCE_NAME, METRIC_MEASURE_FILE_NAME, measures, exportFielPathList);
            //导出指标条件
            List<MetricDimFilter> filters = metricDimFilterService.getDataByMetricIds(metricIds).stream().peek(x->x.setTenantId(null)).collect(Collectors.toList());
            writeToFs(parentDir, METRIC_RESOURCE_NAME, METRIC_DIM_FILTER_FILE_NAME, filters, exportFielPathList);
        }
        if (metricTopResourceOp.isPresent() || datamodelTopResourceOp.isPresent()) {
            if (datamodelTopResourceOp.isPresent()) {
                DiopResourceDto datamodelTopResource = datamodelTopResourceOp.get();
                List<DiopResourceDto> datamodelResources = datamodelTopResource.getExtra();
                exportDatamodelIds.addAll(datamodelResources.stream().map((res) -> Long.parseLong(res.getName())).collect(Collectors.toSet()));
            }
            //导出数据模型
            List<Datamodel> datamodels = datamodelService.getDatamodelByIds(exportDatamodelIds).stream().peek(x->x.setTenantId(null)).collect(Collectors.toList());
            sqlStatementIds.addAll(datamodels.stream().map(Datamodel::getSqlRefId).collect(Collectors.toSet()));
            writeToFs(parentDir, DATAMODEL_RESOURCE_NAME, DATAMODLE_FILE_NAME, datamodels, exportFielPathList);
            //导出数据源
            Set<Long> dsIds = datamodels.stream().map(Datamodel::getDsRefId).collect(Collectors.toSet());
            List<DataSource> dataSources = dataSourceService.exportDataSourceWithIds(dsIds).stream().peek(x->x.setTenantId(null)).collect(Collectors.toList());
            writeToFs(parentDir, DATAMODEL_RESOURCE_NAME, DATASOURCE_FILE_NAME, dataSources, exportFielPathList);
            //导出ER数据
            Set<Long> erRelationIds = datamodels.stream().map(Datamodel::getErRefId).collect(Collectors.toSet());
            List<ErRelation> erRelations = erRelationService.getDataByIds(erRelationIds).stream().peek(x->x.setTenantId(null)).collect(Collectors.toList());
            writeToFs(parentDir, DATAMODEL_RESOURCE_NAME, ER_RELATION_FILE_NAME, erRelations, exportFielPathList);
            //导出数据模型字段信息
            List<DatamodelColumn> columns = datamodelColumnService.getColumnsByDatamodelIds(exportDatamodelIds).stream().peek(x->x.setTenantId(null)).collect(Collectors.toList());
            writeToFs(parentDir, DATAMODEL_RESOURCE_NAME, DATAMODEL_COLUMN_FILE_NAME, columns, exportFielPathList);
        }
        //导出SQL语句信息
        if (!sqlStatementIds.isEmpty()) {
            List<SqlStatement> sqls = sqlStatementService.getDataByIds(sqlStatementIds).stream().peek(x->x.setTenantId(null)).collect(Collectors.toList());
            writeToFs(parentDir, DATAMODEL_RESOURCE_NAME, SQL_FILE_NAME, sqls, exportFielPathList);
        }
        if(sqlQueryResourceOp.isPresent()){
            List<SqlQuery> sqlQueries =sqlQueryService.list().stream().peek(x->x.setTenantId(null)).collect(Collectors.toList());
            writeToFs(parentDir, SQL_QUERY_RESOURCE_NAME, SQL_QUERY_FILE_NAME, sqlQueries, exportFielPathList);
            Set<Long> dataSourceIds = sqlQueries.stream().map(SqlQuery::getDataSourceId).collect(Collectors.toSet());
            List<DataSource> dataSources = dataSourceService.exportDataSourceWithIds(dataSourceIds).stream().peek(x->x.setTenantId(null)).collect(Collectors.toList());
            writeToFs(parentDir, SQL_QUERY_RESOURCE_NAME, DATASOURCE_FILE_NAME, dataSources, exportFielPathList);
            List<SqlQueryGroup> sqlQueryGroups =sqlQueryGroupService.list().stream().peek(x->x.setTenantId(null)).collect(Collectors.toList());
            writeToFs(parentDir, SQL_QUERY_RESOURCE_NAME, SQL_QUERY_GROUP_FILE_NAME, sqlQueryGroups, exportFielPathList);
        }
        //写入导出列表
        FileUtil.writeLines(exportFielPathList, listFile, "utf-8");
        return parentDir;
    }

    public void writeToFs(String parentDir, String resourceType, String fileName, List<?> content, List<String> exportFilePaths) {
        if (content != null && !content.isEmpty()) {
//            for (Object obj : content) {
//                BeanUtil.setFieldValue(obj, "tenantId", null);
//            }
            String pkgPath = resourceType + File.separator + fileName + ".json";
            File file = FileUtil.touch(parentDir + File.separator + pkgPath);
            String jsonContent = JsonMapper.toNonNullJson(content);
            FileUtil.writeString(jsonContent, file, StandardCharsets.UTF_8);
            exportFilePaths.add(pkgPath);
        }
    }

    public String createExportDir(String dateStr, String timeStr,String tenantId) {
        StringBuilder dir = new StringBuilder(StrUtil.replace(diopConfiguration.getExportFileStoragePath(),DIOP_STR,DIOP_STR+"/"+tenantId));
        dir.append(File.separator);
        dir.append(dateStr);
        dir.append(File.separator);
        dir.append(timeStr);
        FileUtil.mkdir(dir.toString());
        return dir.toString();
    }

    public void createSelfdescriptionFile(String parentDir, String dateStr, String timeStr) {
        File descriptionFile = FileUtil.touch(parentDir + File.separator + SELF_DESCRIPTION_FILENAME);
        FileWriter writer = new FileWriter(descriptionFile);
        List<String> contents = Lists.newArrayList();
        contents.add(diopConfiguration.getServiceId() + "_" + dateStr + "_" + timeStr);
        contents.add(diopConfiguration.getAppCode());
        contents.add(diopConfiguration.getServiceId());
        contents.add(diopConfiguration.getAppVersion());
        contents.add(diopConfiguration.getCompatibleVersionMax());
        contents.add(diopConfiguration.getCompatibleVersionMin());
        writer.appendLines(contents);
    }

    public File createListfile(String parentDir) {
        return FileUtil.touch(parentDir + File.separator + LIST_FILE_FILENAME);
    }

}
